(* Release 3.00 *)

(*    c_str - common string functions *)

(*    Copyright (C) 1989 Jensen and Partners. All Rights Reserved*)
(*    Written by Simon Knight.*)

(*    Copyright (C) 1991 Psion PLC. All Rights Reserved*)
(*    Modified by NSM.*)

include "corelib.inc"

module cstr

segment _TEXT(CODE,28H)

(*    string -  string manipulation routines for signed character srtings *)

(*****************************************************************************)
section
segment _TEXT(CODE,28H)

public _strcpy :

(* strcpy *)

    push cx
    push dx
    push di
    push si
    mov  di, bx
    mov  si, bx
    mov  dx, ax
    mov  cx, -1
    sub  al, al
repne ; scasb
    not  cx
    mov  di, dx
    shr  cx, 1
rep; movsw
    adc  cx, cx
rep; movsb
    mov  ax, dx    (* return destination address *)
    pop  si
    pop  di
    pop  dx
    pop  cx
    ret  0


(***************************************************************************)
section
segment _TEXT(CODE,28H)

public _strcat :

(*
char *strcat(char *dest, const char *source)

Appends source to dest.
returns destination address.
*)

    push cx
    push dx
    push di
    push si
    mov  si, bx
    mov  di, ax
    mov  dx, ax
    mov  cx, 0FFFFH
    mov  al, 0
repne ; scasb                 (* find end of destination string *)
    dec  di
catLoop:
    lodsw
    or   al, al
    jz   endOfString
    stosw
    or   ah, ah
    loopne  catLoop
    jmp  catDone
endOfString:
    stosb
catDone:
    mov  ax, dx             (* return destination address *)
    pop  si
    pop  di
    pop  dx
    pop  cx
    ret  0

(*************************************************************************)
section
segment _TEXT(CODE,28H)

public _stpcpy :

(*
char *stpcpy(char *dest, const char *source)

Appends string source to string dest.
Returns destination address + strlen
.
*)

    push cx
    push di
    push si
    mov  si, bx
    mov  di, ax
    mov  cx, 0FFFFH
pcpyLoop:
    lodsw
    or   al, al
    jz   endOfString
    stosw
    or   ah, ah
    loopne  pcpyLoop
    jmp  pcpyDone
endOfString:
    stosb
pcpyDone:
    mov  ax, di               (* return address of terminating zero *)
    dec  ax
    pop  si
    pop  di
    pop  cx
    ret  0

(**************************************************************************)

section
segment _TEXT(CODE,28H)

public  _strchr :

    push cx
    push dx
    push di
    mov  di, ax
    mov  dx, ax
    mov  cx, -1
    sub  al, al
repne ; scasb
    not  cx
    mov  di, dx
    mov  al, bl
repne; scasb
    jnz   notFound
    dec  di                   (* return address of target *)
    mov  ax, di
chrDone:
    pop  di
    pop  dx
    pop  cx
    ret  0
notFound:
    sub  ax, ax
    jmp  chrDone

(**************************************************************************)
section
segment _TEXT(CODE,28H)

public  _strcmp :
public  _strcoll :

(* strcmp *)

    push cx
    push di
    push si
    mov  si, ax
    mov  di, bx
    mov  cx, -1
    sub  ax, ax
repne ; scasb
    not  cx
    mov  di, bx
repe; cmpsb
    jz   ncmpDone
    jb   ncmpLess
ncmpMore:
    inc  ax
    jmp  ncmpDone
ncmpLess:
    dec  ax
ncmpDone:
    pop  si
    pop  di
    pop  cx
    ret  0

(***************************************************************************)
section
segment _TEXT(CODE,28H)

public _strcspn :

(*
int strcspn(const char *s1, const char *s2)

Returns length of initial segment of s1 not containing characters from s2.
*)

    push cx
    push di
    push si
    mov  si, ax
    mov  cx, 0FFFFH
cspnLoop:
    lodsb
    or   al, al            (* end of string ? *)
    je   cspnDone
    mov  di, bx
ctLoop:                    (* search second string *)
    mov  ah, [di]
    cmp  al, ah
    je   cspnDone          (* Found Matching character - get out *)
    inc  di
    or   ah, ah
    jne  ctLoop            (* repeat compare loop *)
    loop cspnLoop          (* do next charcter in target string *)
cspnDone:
    not  cx                (* return length to Matching character *)
    mov  ax, cx
    pop  si
    pop  di
    pop  cx
    ret  0

(***************************************************************************)
section
segment _TEXT(CODE,28H)


extrn  _malloc      (* used to allocate memory for new string in strdup *)


public _strdup :

(*
char  *strdup(const char *s)

Allocates memory with malloc and copies string s to that address.
Returns pointer to new copy or NULL if call to malloc failed.
*)

    push bx
    push cx
    push di
    push si
    mov  si, ax
    mov  di, ax
    mov  cx, 0FFFFH
    sub  ax, ax
repne ; scasb            (* get length of string plus terminator 0 *)
    not  cx
    push cx
    mov  ax, cx
    call _malloc
    pop  cx
    or   ax, ax
    je   dupnocopy
    mov  di, ax
rep ;  movsb             (* copy string *)
dupnocopy:
    pop  si
    pop  di
    pop  cx
    pop  bx
    ret  0


(**************************************************************************)
section
segment _TEXT(CODE,28H)

public _stricmp :

(*
int stricmp(const char	*s1, const char  *s2)

Unsigned case insensitive comparison of s1 and s2.
Returns -1 if s1<s2, 0 if s1=s2 and 1 if s1 > s2.
*)

    push cx
    push dx
    push di
    push si
    mov  si, ax
    mov  di, bx
    mov  cx, 0FFFFH
    sub  ax, ax
icmpLoop:
    lodsb
    cmp  al, 'z'          (* convert to upper if lower *)
    ja   inotlowa
    cmp  al, 'a'
    jb   inotlowa
    sub  al, 20H
inotlowa:
    mov  bl, [di]
    inc  di
    cmp  bl, 'z'          (* convert to upper if lower *)
    ja   inotlowb
    cmp  bl, 'a'
    jb   inotlowb
    sub  bl, 20H
inotlowb:
    cmp  al, bl
    jb   iLessThan
    ja   iGreaterThan
    or   al, al
    loopnz icmpLoop
icmpSame:
    sub ax, ax            (* return 0 *)
    jmp  icmpDone
iLessThan:
    mov  ax, -1           (* return -1 *)
    jmp  icmpDone
iGreaterThan:
    mov  ax, 1
icmpDone:
    pop  si
    pop  di
    pop  dx
    pop  cx
    ret  0


(**************************************************************************)
section
segment _TEXT(CODE,28H)

public _strlen :

(*
size_t	strlen(const char *s)

Returns length of s excluding terminating zero.
*)


    push cx
    push di
    mov  di, ax
    mov  cx, 0FFFFH
    sub  al, al
repne ;  scasb             (* get length of string *)
    not  cx
    mov  ax, cx
    dec  ax                (* return value is length of string *)
    pop  di
    pop  cx
    ret  0

(**************************************************************************)
section
segment _TEXT(CODE,28H)

public _strlwr :

(*
char  *strlwr(char *s)

Converts string s to lower case.
returns  s.
*)

    push bx
    push cx
    push di
    push si
    mov  si, ax
    mov  di, ax
    mov  bx, ax
    mov  cx, 0FFFFH
lwrLoop:
    lodsb                    (* load  *)
    or   al, al
    je   lwrDone             (* end of string *)
    cmp  al, 'Z'
    ja   lwrNoChange
    cmp  al, 'A'
    jb   lwrNoChange
    add  al, 20H             (* convert *)
lwrNoChange:
    stosb                    (* store *)
    loop lwrLoop
lwrDone:
    mov  ax, bx              (* return string address *)
    pop  si
    pop  di
    pop  cx
    pop  bx
    ret  0

(**************************************************************************)
section
segment _TEXT(CODE,28H)

public _strncat :

(*
char *strncat(char *dest, const char *source, int  num)

Concatenates dest and upto num characters from source.
returns destination address.
*)

    push dx
    push di
    push si
    mov  si, bx
    mov  di, ax
    mov  dx, ax
    mov  bx, cx
    mov  cx, 0FFFFH
    sub  al, al
repne ; scasb                (* find end of destination string *)
    dec  di
    mov  cx, bx              (* count *)
    jcxz ncatDone
ncatLoop:
    lodsb                    (* use lodsb/stosb to avoid mem/immed compare *)
    stosb
    or   al, al
    je   ncatDone
    loop ncatLoop
    mov  byte [di], 0        (* terminate new string *)
ncatDone:
    mov  ax, dx              (* return destination address *)
    pop  si
    pop  di
    pop  dx
    ret  0

(*****************************************************************************)
section
segment _TEXT(CODE,28H)

public  _strncmp :

(* strncmp *)

    push dx
    push di
    push si
    mov  si, ax
    mov  di, bx
    mov  dx, bx
    mov  bx, cx
    sub  ax, ax
repne ; scasb
    jcxz stringLong
    sub  bx, cx
stringLong:
    mov  cx, bx
    mov  di, dx
repe; cmpsb
    jz   ncmpDone
    jl   ncmpLess
ncmpMore:
    inc  ax
    jmp  ncmpDone
ncmpLess:
    dec  ax
ncmpDone:
    pop  si
    pop  di
    pop  dx
    ret  0

(**************************************************************************)
section
segment _TEXT(CODE,28H)

public _strncpy :

(*
char *strcpy(char *dest, const char *source, int num)

copies up to num characters to dest.
returns destination address.
*)

    push dx
    push di
    push si
    mov  si, bx
    mov  di, ax
    mov  dx, ax
    jcxz ncpyDone
ncpyLoop:
    lodsb                      (* avoids mem/immed compare *)
    stosb
    or   al, al
    loopne ncpyLoop
ncpyDone:
rep ; stosb
    mov  ax, dx               (* return destination address *)
    pop  si
    pop  di
    pop  dx
    ret  0

(**************************************************************************)
section
segment _TEXT(CODE,28H)

public _strxfrm :

    push dx
    push di
    push si
    mov  si, bx
    mov  di, ax
    mov  dx, cx
    jcxz ncpyDone
ncpyLoop:
    lodsb                      (* avoids mem/immed compare *)
    stosb
    or   al, al
    je   ncpyDone
    loop ncpyLoop
    sub  al, al
    stosb
ncpyDone:
    mov  ax, dx
    inc  ax
    pop  si
    pop  di
    pop  dx
    ret  0


(**************************************************************************)
section
segment _TEXT(CODE,28H)

public _mbstowcs :
public _wcstombs :

    push dx
    push di
    push si
    mov  si, bx
    mov  di, ax
    mov  bx, cx
    jcxz ncpyDone
ncpyLoop:
    lodsb                      (* avoids mem/immed compare *)
    stosb
    or   al, al
    je   ncpyDone
    loop ncpyLoop
ncpyDone:
    mov  ax, bx
    sub  ax, cx
    pop  si
    pop  di
    pop  dx
    ret  0


(**************************************************************************)
section
segment _TEXT(CODE,28H)

public _strnicmp :

(*
int strnicmp(const char  *s1, const char  *s2, int num)


Compares up to num characters.
Returns < 0 if s1<s2, 0 if s1=s2 and > 0 if s1 > s2.
*)

    push dx
    push di
    push si
    mov  si, ax
    mov  di, bx
    sub  ax, ax
    jcxz nicmpDone
nicmpLoop:
    lodsb
    cmp  al, 'z'        (* convert to upper if lower *)
    ja   notlowa
    cmp  al, 'a'
    jb   notlowa
    sub  al, 20H
notlowa:
    mov  bl, [di]
    inc  di             (* convert to upper if lower *)
    cmp  bl, 'z'
    ja   notlowb
    cmp  bl, 'a'
    jb   notlowb
    sub  bl, 20H
notlowb:
    cmp  al, bl
    jl   nicmpLessThan
    jg   nicmpGreaterThan
    or   al, al
    loopne nicmpLoop
nicmpSame:
    sub  ax, ax         (* return 0 *)
    jmp  nicmpDone
nicmpLessThan:
    mov  ax, -1         (* return -1 *)
    jmp  nicmpDone
nicmpGreaterThan:
    mov  ax, 1          (* return 1 *)
nicmpDone:
    pop  si
    pop  di
    pop  dx
    ret  0

(*****************************************************************************)
section
segment _TEXT(CODE,28H)

public _strnset :

(*
char *strnset(char *s, char  c, unsigned num)

sets up to num characters to c
returns s.
*)

    push di
    mov  di, ax
    push ax
    mov  al, bl
    jcxz nsetDone
nsetLoop:
    cmp  byte [di], 0
    je   nsetDone
    stosb
    loop nsetLoop
nsetDone:
    pop  ax                (* return string address *)
    pop  di
    ret  0

(*****************************************************************************)
section
segment _TEXT(CODE,28H)

public _strpbrk :

(*
int strpbrk(const char *s1, const char *s2)

returns address of first occurrence of any character from s2.
*)

    push cx
    push di
    push si
    mov  si, ax
    mov  cx, 0FFFFH
pbrkLoop:
    lodsb
    or   al, al            (* end of string ? *)
    je   pbrkDone
    mov  di, bx
ktLoop:                    (* search in second string *)
    mov  ah, [di]
    cmp  al, ah
    je   pbrkFound         (* Found Matching character *)
    inc  di
    or   ah, ah
    jne  ktLoop            (* repeat *)
    loop pbrkLoop          (* do next character in target string *)
pbrkDone:
    sub  ax, ax
    jmp  pbrkFinish
pbrkFound:
    dec  si                (* return address of first Match *)
    mov  ax, si
pbrkFinish:
    pop  si
    pop  di
    pop  cx
    ret  0

(*******************************************************************************)
section
segment _TEXT(CODE,28H)

public _strrchr :

(*
char *strrchr(const char *s, int c)

searches string for last occurrence c.
returns address of target byte if Found.
*)

    push cx
    push dx
    push di
    mov  di, ax
    mov  dx, ax
    mov  cx, 0FFFFH
    sub  al, al
repne ;  scasb                (* find end of string *)
    dec  di
    std
    mov  al, bl
    not  cx
repne  ;  scasb               (* search *)
    je   rchrFound
rchrnotFound:
    sub  ax, ax               (* return NULL *)
    jmp  rchrDone
rchrFound:
    inc  di                   (* return address of Matching character *)
    mov  ax, di
rchrDone:
    cld
    pop  di
    pop  dx
    pop  cx
    ret  0

(******************************************************************************)
section
segment _TEXT(CODE,28H)

public _strrev :

(*
char *strrev(char *s)

reverses s.
*)

    push cx
    push dx
    push di
    push si
    mov  si, ax
    mov  di, ax
    mov  dx, ax
    sub  al, al
    mov  cx, 0FFFFH
repne ;  scasb
    cmp  cx, 0FFFDH           (* find end of string *)
    jae  revDone
    sub  di, 2
    mov  cx, 0FFFFH
revLoop:
    mov  al, [di]             (* get end character *)
    movsb                     (* move beginning to end *)
    sub  di, 2
    mov  [si][-1], al         (* store end character *)
    cmp  si, di
    jae  revDone              (* exit at crossover *)
    loop revLoop
revDone:
    mov  ax, dx               (* return address of string *)
    pop  si
    pop  di
    pop  dx
    pop  cx
    ret  0

(*****************************************************************************)
section
segment _TEXT(CODE,28H)

public _strset :

(*
char *strset(char *s, int  c)

sets characters to c
returns s.
*)

    push cx
    push dx
    push di
    mov  di, ax
    mov  dx, ax
    mov  cx, -1
    sub  al, al
repne ; scasb
    not  cx
    dec  cx
    mov  al, bl
    mov  ah, al
    mov  di, dx
    shr  cx, 1
rep; stosw
    adc  cx, cx
rep; stosb
    mov  ax, dx            (* return address of string *)
    pop  di
    pop  dx
    pop  cx
    ret  0

(********************************************************************************)
section
segment _TEXT(CODE,28H)

public _strspn :

(*
int strspn(const char *s1, const char *s2)

returns length of initial segment of s1 containing characters from s2.
*)

    push cx
    push dx
    push di
    push si
    mov  si, ax
    mov  dx, bx
    mov  cx, 0FFFFH
spnLoop:
    lodsb
    or   al, al            (* end of string ? *)
    je   spnDone
    mov  di, dx

stLoop:                    (* search second string *)
    mov  ah, [di]
    cmp  al, ah
    je   spnMatch          (* Found Matching character *)
    inc  di
    or   ah, ah
    jne  stLoop            (* repeat *)
    jmp  spnDone           (* no Match Found *)
spnMatch:
    loop spnLoop           (* do next charcter in target string *)
spnDone:
    not  cx                (* return length to non Matching character *)
    mov  ax, cx
    pop  si
    pop  di
    pop  dx
    pop  cx
    ret  0

(*****************************************************************************)
section
segment _TEXT(CODE,28H)

public _strstr :

(*
char *strstr(const char *s1, const char *s2)

returns address of first occurrence of s2 in s1, or NULL if not Found.
*)


s1      = -4
s2      = -8
len1    = -10
len2    = -12
locals =   12

    push bp
    mov  bp, sp
    sub  sp, locals
    push cx
    push dx
    push di
    push si
    mov  di, bx
    mov  [bp][s2], bx
    mov  [bp][s1], ax
    mov  cx, 0FFFFH         (* get length of s2 - saves time in loop *)
    sub  al, al
repne ;  scasb
    not  cx
    dec  cx
    jcxz s2Null
    mov  [bp][len2], cx
    mov  di, [bp][s1]
    mov  dx, di
    mov  cx, 0FFFFH         (* get length of s2 - saves time in loop *)
repne ;  scasb
    not  cx
    dec  cx
    mov  si, [bp][s2]
    mov  di, dx
    mov  dx, si
    mov  al, [si]
strLoop:
repne ; scasb
   jnz  strNotFound
   push cx
   push di
   dec  di
   mov  cx, [bp][len2]
repe ; cmpsb
   pop  di
   pop  cx
   je   strFound
   mov  si, dx
   jmp  strLoop
strNotFound:
   sub  ax, ax
   jmp strFinish
strFound:
    mov  ax, di
    dec  ax
strFinish:
    pop  si
    pop  di
    pop  dx
    pop  cx
    mov  sp, bp
    pop  bp
    ret  0

s2Null:
    mov  ax, [bp][s1]
    jmp  strFinish

(****************************************************************************)
section
segment _TEXT(CODE,28H)

public _strupr :

(*
char  *strupr(char *s)

converts lower case characters to upper case.
returns  s.
*)

    push bx
    push cx
    push di
    push si
    mov  si, ax
    mov  di, ax
    mov  bx, ax
    mov  cx, 0FFFFH
uprLoop:
    lodsb
    or   al, al
    je   uprDone             (* end of string Found *)
    cmp  al, 'z'             (* change if lower *)
    ja   uprNoChange
    cmp  al, 'a'
    jb   uprNoChange
    sub  al, 20H             (* convert to upper *)
uprNoChange:
    stosb
    loop uprLoop
uprDone:                     (* return address of string *)
    mov  ax, bx
    pop  si
    pop  di
    pop  cx
    pop  bx
    ret  0

(***************************************************************************)

section

segment _TEXT(CODE,28H)


public _memcpy :

(*
void *memcpy(void *dest, void *source, unsigned num)

Copies num bytes from source to dest (Does not handle any overlap).
Returns destination address.
*)

    push di
    push si
    mov  si, bx
    mov  di, ax
    jcxz noMove
    test al, 1
    jz   evenAdd            (* word align *)
    movsb
    dec  cx
evenAdd:
    shr  cx, 1
rep ; movsw                 (* move words *)
    adc  cx, cx
rep ; movsb                 (* move odd byte *)
noMove:
    pop  si
    pop  di
    ret  0

(****************************************************************************)
section
segment _TEXT(CODE,28H)

public _memccpy :

(*
void *memccpy(void *dest, void *source, unsigned char c, unsigned num)

Copying stops after copying c or after num bytes.
returns destination address.
*)

    push dx
    push di
    push si
    mov  si, bx
    mov  di, ax
    xchg cx, dx
    jcxz ccpyDone
ccpyLoop:
    lodsb                   (* use lods/stos to get byte in al for compare *)
    stosb
    cmp  al, dl             (* faster than movs + reg/mem cmp *)
    je   ccpyDone
    loop ccpyLoop
    sub  ax, ax
    jmp  ccopyExit
ccpyDone:
    mov  ax, di
ccopyExit:
    pop  si
    pop  di
    pop  dx
    ret  0

(****************************************************************************)
section
segment _TEXT(CODE,28H)

public _memchr :

(*
void *memchr(void *s, char c, unsigned num)

searches num bytes for c.
returns source address.
*)


    push di
    mov  di, ax
    mov  al, bl
    jcxz chrFound
repne ;  scasb
    je   chrFound
    sub  ax, ax       (* not found - return value NULL *)
    jmp  chrDone
chrFound:
    dec  di           (* found - return address. *)
    mov  ax, di
chrDone:
    pop  di
    ret  0


(****************************************************************************)
section
segment _TEXT(CODE,28H)

public _memset :

(*
void *memset(void *s, char c, unsigned num)

Sets num bytes to c.
Returns buffer address.
*)

    push dx
    push di
    mov  di, ax
    mov  dx, ax
    mov  al, bl
    jcxz noMove
    mov  ah, al
    test di, 1
    jz   evenAdd
    stosb              (* word align *)
    dec  cx
evenAdd:
    shr  cx, 1
rep  ; stosw           (* move words *)
    adc  cx, cx
rep  ; stosb           (* move odd byte *)
noMove:
    mov  ax, dx
    pop  di
    pop  dx
    ret  0

(******************************************************************************)
section
segment _TEXT(CODE,28H)

public _memcmp :
(*
int  memcmp(void *s1, void *s2, unsigned num)

Performs unsigned comparison of s1 and s2 and returns < 0 if s1<s2,
0 if s1=s2 and > 0 if s1 > s2.
*)

    push di
    push si
    mov  si, ax
    mov  di, bx
    sub  ax, ax                  (* same - return value is 0 *)
    jcxz cmpDone
repe ; cmpsb                     (* compare *)
    jz   cmpDone
    sbb  ax, ax
    sbb  ax, -1
cmpDone:
    pop  si
    pop  di
    ret  0

(***************************************************************************)
section
segment _TEXT(CODE,28H)

public _memicmp :

(*
int memicmp(void *s1, void *s2, unsigned num)

Performs case insensitive comparison of s1 and s2, returning < 0 if s1<s2,
0 if s1=s2 and > 0 if s1 > s2.
*)

    push di
    push si
    mov  si, ax
    mov  di, bx
    sub  ax, ax
    jcxz icmpSame
icmpLoop:
    lodsb
    cmp  al, 'z'          (* if lower convert to upper *)
    ja   inotlowa
    cmp  al, 'a'
    jb   inotlowa
    sub  al, 20H
inotlowa:
    mov  bl, [di]
    inc  di
    cmp  bl, 'z'          (* if lower convert to upper *)
    ja   inotlowb
    cmp  bl, 'a'
    jb   inotlowb
    sub  bl, 20H
inotlowb:
    sub  al, bl
    jne  icmpDifferent
    loop icmpLoop
    sub  ax, ax
    jmp  icmpSame
icmpDifferent:
    mov  al, 0
    sbb  al, al
    sbb  al, -1
    cbw
icmpSame:
    pop  si
    pop  di
    ret  0

(***************************************************************************)
section
segment _TEXT(CODE,28H)

public _memwset :

(*
void *memwset(void *s, char c, unsigned num)

Sets num words to c.
Returns buffer address.
*)

    push dx
    push di
    mov  di, ax
    mov  ax, bx
    mov  dx, di
rep ;  stosw
    mov  ax, dx
    pop  di
    pop  dx
    ret  0

(*****************************************************************************)
section
segment _TEXT(CODE,28H)

public _memwcpy :

(*
void *memwcpy(void *dest, void *source, unsigned num)

Copies num words form source to dest (Does not handle overlap).
Returns destination address.
*)

    push di
    push si
    mov  si, bx
    mov  di, ax
rep ; movsw
    pop  si
    pop  di
    ret  0

(*****************************************************************************)
section
segment _TEXT(CODE,28H)

public _setmem :

(*
void setmem(void *s, unsigned num, char c)

ets num bytes to c.
Returns nothing.
*)


    push di
    mov  di, ax
    mov  al, cl
    mov  cx, bx
(*%E *)
    jcxz noMove
    mov  ah, al
    test di, 1
    jz   evenAdd
    stosb
    dec  cx                    (* word align *)
evenAdd:
    shr  cx, 1
rep ;  stosw                   (* move words *)
    adc  cx, cx
rep ;  stosb                   (* move odd byte *)
noMove:
    pop  di
    ret  0

end



